﻿namespace MDXStudio.QueryEditor
{
    using MDXParser;
    using MDXStudio;
    using SearchableControls;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    public sealed class MdxEditor : SearchableRichTextBox
    {
        private IContainer components;
        private MDXParser.ColorCoding m_ColorCoding;
        private bool mChangingFlag;
        private bool mInOnUndo;
        private string mPrevMDX;
        private Stack<UndoRedoEntry> mRedoStack;
        private ToolTip mToolTip;
        private Stack<UndoRedoEntry> mUndoStack;

        public MdxEditor()
        {
            this.InitializeComponent();
            this.AllowDrop = true;
            this.InitUndoRedoBuffer();
        }

        public MdxEditor(IContainer container)
        {
            container.Add(this);
            this.InitializeComponent();
            this.AllowDrop = true;
            base.AcceptsTab = true;
            this.InitUndoRedoBuffer();
            this.mToolTip = new ToolTip(base.Container);
        }

        private void AdjustTextColors(IList<TextSegment> pList, int pStart, int pEnd)
        {
            Color black = Color.Black;
            for (int i = pStart; i <= pEnd; i++)
            {
                switch (pList[i].HighlightType)
                {
                    case eHighlightType.BlockComment:
                        black = Color.Green;
                        break;

                    case eHighlightType.KeyWord:
                        black = Color.Blue;
                        break;

                    case eHighlightType.LineComment:
                        black = Color.Green;
                        break;

                    case eHighlightType.Number:
                        black = Color.Magenta;
                        break;

                    case eHighlightType.Operator:
                        black = Color.Gray;
                        break;

                    case eHighlightType.PlainText:
                        black = Color.Black;
                        break;

                    case eHighlightType.String:
                        black = Color.Red;
                        break;

                    case eHighlightType.SystemFunction:
                        black = Color.DarkRed;
                        break;
                }
                this.AdjustTextSegment(pList[i].StartPosition, pList[i].Lenth, black);
                if (i == pEnd)
                {
                    base.SelectionStart += pList[i].Lenth;
                    this.SelectionLength = 0;
                }
            }
        }

        private void AdjustTextSegment(int pStartPoint, int pLenth, Color pColor)
        {
            base.SelectionStart = pStartPoint;
            this.SelectionLength = pLenth;
            base.SelectionColor = pColor;
        }

        private void CompleteList(IList<TextSegment> pList, string pText)
        {
            if (pList.Count > 0)
            {
                int num2;
                int num4;
                if (pList[0].StartPosition > 0)
                {
                    pList.Insert(0, new TextSegment(eHighlightType.PlainText, 0, pList[0].StartPosition));
                }
                int index = 1;
                while (index < pList.Count)
                {
                    num4 = pList[index - 1].StartPosition + pList[index - 1].Lenth;
                    if (pList[index].StartPosition > num4)
                    {
                        int pStartPosition = num4;
                        num2 = pList[index].StartPosition - pStartPosition;
                        pList.Insert(index, new TextSegment(eHighlightType.PlainText, pStartPosition, num2));
                        index++;
                    }
                    index++;
                }
                num4 = pList[index - 1].StartPosition + pList[index - 1].Lenth;
                num2 = pText.Length - num4;
                if (num4 <= pText.Length)
                {
                    pList.Add(new TextSegment(eHighlightType.PlainText, num4, num2));
                }
            }
            else
            {
                pList.Add(new TextSegment(eHighlightType.PlainText, 0, pText.Length));
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (this.components != null))
            {
                this.components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void FilterList(List<TextSegment> pList)
        {
            pList.Sort(new MyTextSegmentComparer());
            for (int i = 0; i < pList.Count; i++)
            {
                int index = i + 1;
                while (index < pList.Count)
                {
                    if (!pList[index].StartIsIn(pList[i]))
                    {
                        break;
                    }
                    pList.RemoveAt(index);
                }
            }
        }

        private void FindChangedText(IList<TextSegment> pTextListPrev, IList<TextSegment> pTextList, out int oChangedStart, out int oChangedEnd)
        {
            bool flag = false;
            oChangedStart = 0;
            oChangedEnd = pTextList.Count - 1;
            int num = Math.Min(pTextListPrev.Count, pTextList.Count);
            for (int i = 0; i < num; i++)
            {
                if (!pTextListPrev[i].Equals(pTextList[i]))
                {
                    oChangedStart = i;
                    flag = true;
                    break;
                }
            }
            if (!flag)
            {
                oChangedStart = num;
            }
            else
            {
                int num3 = pTextListPrev.Count - 1;
                for (int j = pTextList.Count - 1; (num3 >= oChangedStart) && (j >= oChangedStart); j--)
                {
                    TextSegment segment = pTextListPrev[num3];
                    TextSegment segment2 = pTextList[j];
                    oChangedEnd = j;
                    if (segment.Lenth != segment2.Lenth)
                    {
                        break;
                    }
                    if (segment.HighlightType != segment2.HighlightType)
                    {
                        return;
                    }
                    num3--;
                }
            }
        }

        public int FindMatch(char c1, char c2)
        {
            int num = base.SelectionStart - 1;
            int num2 = 1;
            while (num >= 0)
            {
                if (this.Text[num] == c2)
                {
                    num2--;
                    if (num2 == 0)
                    {
                        return num;
                    }
                }
                else if (this.Text[num] == c1)
                {
                    num2++;
                }
                num--;
            }
            return -1;
        }

        public string GetCurrentWord()
        {
            return this.GetWord(1, true, false);
        }

        public string GetPrecedingWord()
        {
            return this.GetPrecedingWord(1);
        }

        public string GetPrecedingWord(int cwords)
        {
            return this.GetWord(cwords, false, true);
        }

        private string GetWord(int cwords, bool stopatdot, bool skiptrailingwhitespaces)
        {
            int index = base.SelectionStart - 1;
        Label_0009:
            if (skiptrailingwhitespaces)
            {
                while ((index >= 0) && char.IsWhiteSpace(this.Text, index))
                {
                    index--;
                }
            }
            if (index < 0)
            {
                return null;
            }
            int startIndex = index;
            bool flag = false;
            while (startIndex >= 0)
            {
                char c = this.Text[startIndex];
                if (flag)
                {
                    if (c == '[')
                    {
                        flag = false;
                    }
                }
                else if (c == ']')
                {
                    flag = true;
                }
                else if ((stopatdot && (c == '.')) || (((c != '.') && (c != '_')) && !char.IsLetterOrDigit(c)))
                {
                    break;
                }
                startIndex--;
            }
            cwords--;
            if (cwords != 0)
            {
                index = startIndex;
                goto Label_0009;
            }
            startIndex++;
            return this.Text.Substring(startIndex, (index - startIndex) + 1);
        }

        public void HideToolTip()
        {
            this.mToolTip.Hide(this);
        }

        private void InitializeComponent()
        {
            base.SuspendLayout();
//            this.Font = FontUtils.FontCreate("Courier New", 8f, FontStyle.Regular);
            base.WordWrap = false;
            base.ResumeLayout(false);
        }

        private void InitUndoRedoBuffer()
        {
            this.mUndoStack = new Stack<UndoRedoEntry>();
            this.mRedoStack = new Stack<UndoRedoEntry>();
            this.mUndoStack.Push(new UndoRedoEntry(base.Rtf, base.SelectionStart, this.SelectionLength, null));
        }

        private void InsertTextFromClipboard()
        {
            string str;
            IDataObject dataObject = Clipboard.GetDataObject();
            if (dataObject.GetDataPresent(DataFormats.UnicodeText))
            {
                str = dataObject.GetData(DataFormats.UnicodeText).ToString();
                this.SelectedText = str;
            }
            else if (dataObject.GetDataPresent(DataFormats.Text))
            {
                str = dataObject.GetData(DataFormats.Text).ToString();
                this.SelectedText = str;
            }
        }

        public void LoadFile(string pFilePath)
        {
            base.LoadFile(pFilePath);
            this.InitUndoRedoBuffer();
        }

        public void LoadFile(Stream pData, RichTextBoxStreamType pFileType)
        {
            base.LoadFile(pData, pFileType);
            this.InitUndoRedoBuffer();
        }

        public void LoadFile(string pFilePath, RichTextBoxStreamType pFileType)
        {
            base.LoadFile(pFilePath, pFileType);
            this.InitUndoRedoBuffer();
        }

        public void MakeRedo()
        {
            if (this.mRedoStack.Count > 0)
            {
                try
                {
                    MDXStudio.QueryEditor.NativeMethods.NestedLockWindowUpdate(base.Handle);
                    this.mInOnUndo = true;
                    UndoRedoEntry item = this.mRedoStack.Pop();
                    this.mUndoStack.Push(item);
                    base.Rtf = item.RtfText;
                    base.SelectionStart = item.SelectionStart;
                    this.SelectionLength = 0;
                }
                finally
                {
                    this.mInOnUndo = false;
                    MDXStudio.QueryEditor.NativeMethods.NestedUnlockWindowUpdate();
                    this.Refresh();
                }
            }
        }

        public void MakeUndo()
        {
            if (this.mUndoStack.Count > 1)
            {
                try
                {
                    MDXStudio.QueryEditor.NativeMethods.NestedLockWindowUpdate(base.Handle);
                    this.mInOnUndo = true;
                    this.mRedoStack.Push(this.mUndoStack.Pop());
                    UndoRedoEntry entry = this.mUndoStack.Peek();
                    base.Rtf = entry.RtfText;
                    base.SelectionStart = entry.SelectionStart;
                    this.SelectionLength = 0;
                    if ((base.Tag != null) && (base.Tag.GetType() == typeof(WigglyLinesPainter)))
                    {
                        WigglyLinesPainter tag = base.Tag as WigglyLinesPainter;
                        tag.WigglyLines = entry.WigglyLines;
                    }
                }
                finally
                {
                    this.mInOnUndo = false;
                    MDXStudio.QueryEditor.NativeMethods.NestedUnlockWindowUpdate();
                    this.Refresh();
                }
            }
        }

        protected override void OnDragDrop(DragEventArgs pArgs)
        {
            string data = (string) pArgs.Data.GetData(typeof(string));
            this.SelectedText = data;
            base.OnDragDrop(pArgs);
        }

        protected override void OnDragEnter(DragEventArgs pArgs)
        {
            pArgs.Effect = DragDropEffects.Move;
            base.OnDragEnter(pArgs);
        }

        protected override void OnDragOver(DragEventArgs pArgs)
        {
            pArgs.Effect = DragDropEffects.Move;
            base.OnDragOver(pArgs);
        }

        protected override void OnKeyDown(KeyEventArgs pArgs)
        {
            try
            {
                if ((pArgs.KeyCode == Keys.V) && (pArgs.Modifiers == Keys.Control))
                {
                    this.InsertTextFromClipboard();
                    pArgs.Handled = true;
                }
                if ((pArgs.KeyCode == Keys.E) && (pArgs.Modifiers == Keys.Control))
                {
                    pArgs.Handled = true;
                }
                if ((pArgs.KeyCode == Keys.L) && (pArgs.Modifiers == Keys.Control))
                {
                    pArgs.Handled = true;
                }
                if ((pArgs.KeyCode == Keys.R) && (pArgs.Modifiers == Keys.Control))
                {
                    pArgs.Handled = true;
                }
                if (((pArgs.KeyCode == Keys.Back) && (pArgs.Modifiers == Keys.Alt)) || ((pArgs.KeyCode == Keys.Z) && (pArgs.Modifiers == Keys.Control)))
                {
                    this.Undo();
                    pArgs.Handled = true;
                }
                if ((pArgs.KeyCode == Keys.Y) && (pArgs.Modifiers == Keys.Control))
                {
                    this.Redo();
                    pArgs.Handled = true;
                }
                base.OnKeyDown(pArgs);
            }
            catch (Exception exception)
            {
                MessageBox.Show(base.FindForm(), exception.Message + "\n\n" + exception.StackTrace, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
        }

        protected override void OnTextChanged(EventArgs pArgs)
        {
            try
            {
                base.OnTextChanged(pArgs);
                if (!this.mInOnUndo)
                {
                    this.Process();
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show(base.FindForm(), exception.Message + "\n\n" + exception.StackTrace, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
        }

        private void Process()
        {
            if (!this.mChangingFlag)
            {
                try
                {
                    this.mChangingFlag = true;
                    string text = this.Text;
                    int selectionStart = base.SelectionStart;
                    bool flag = true;
                    if (this.mPrevMDX != null)
                    {
                        flag = !text.Equals(this.mPrevMDX, StringComparison.InvariantCulture);
                    }
                    this.mPrevMDX = text;
                    WigglyLinesPainter tag = base.Tag as WigglyLinesPainter;
                    if (flag)
                    {
                        if (tag != null)
                        {
                            tag.ClearWigglyLines();
                        }
                        TextBoxSource src = new TextBoxSource(this, new Locator());
                        CubeInfo cb = new CubeInfo();
                        MDXParser.MDXParser parser = new MDXParser.MDXParser(text, src, cb);
                        parser.ColorCode();
                        if (!parser.HasColorCoding())
                        {
                            return;
                        }
                        this.m_ColorCoding = parser.GetColorCoding();
                        string str2 = this.m_ColorCoding.ConvertToRTF(text);
                        try
                        {
                            MDXStudio.QueryEditor.NativeMethods.NestedLockWindowUpdate(base.Handle);
                            int firstVisibleLine = TextBoxAPIHelper.GetFirstVisibleLine(this);
                            if (str2 != null)
                            {
                                base.Rtf = str2;
                            }
                            else
                            {
                                this.ForeColor = Color.Black;
                                this.BackColor = Color.White;
                                base.SelectionBackColor = Color.White;
                                this.Text = text;
                            }
                            base.SelectionStart = selectionStart;
                            int num3 = TextBoxAPIHelper.GetFirstVisibleLine(this);
                            if (num3 > firstVisibleLine)
                            {
                                for (int i = 0; i < (num3 - firstVisibleLine); i++)
                                {
                                    TextBoxAPIHelper.ScrollLineUp(this);
                                }
                            }
                            else
                            {
                                for (int j = 0; j < (firstVisibleLine - num3); j++)
                                {
                                    TextBoxAPIHelper.ScrollLineDown(this);
                                }
                            }
                        }
                        finally
                        {
                            MDXStudio.QueryEditor.NativeMethods.NestedUnlockWindowUpdate();
                        }
                    }
                    WigglyLines wl = null;
                    if (tag != null)
                    {
                        wl = tag.WigglyLines;
                    }
                    this.mUndoStack.Push(new UndoRedoEntry(base.Rtf, selectionStart, 0, wl));
                    this.mRedoStack.Clear();
                }
                finally
                {
                    this.mChangingFlag = false;
                }
            }
        }

        public void Redo()
        {
            this.MakeRedo();
        }

        public void ShowToolTip(string text)
        {
            Point point = TextBoxAPIHelper.PosFromChar(this, base.SelectionStart);
            point.Y += this.Font.Height + 4;
            this.mToolTip.Show(text, this, point);
        }

        public void ShowToolTip(string text, int duration)
        {
            Point point = TextBoxAPIHelper.PosFromChar(this, base.SelectionStart);
            point.Y += this.Font.Height;
            this.mToolTip.Show(text, this, point, duration);
        }

        private void SyntaxAnalysis(List<TextSegment> pList)
        {
            ParserUtil.CheckRegExsLoadList(this.Text, pList);
            if (pList.Count > 1)
            {
                this.FilterList(pList);
            }
            this.CompleteList(pList, this.Text);
        }

        public void Undo()
        {
            this.MakeUndo();
        }

        public bool CanUndo
        {
            get
            {
                return true;
            }
        }

        public MDXParser.ColorCoding ColorCoding
        {
            get
            {
                return this.m_ColorCoding;
            }
        }

        public bool RedoEnabled
        {
            get
            {
                return (this.mRedoStack.Count > 0);
            }
        }

        public bool UndoEnabled
        {
            get
            {
                return (this.mUndoStack.Count > 1);
            }
        }
    }
}

